#include "KKComm.h"
#include "KKRenderGdiPlus.h"
#include <cmath>

Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR ggdiplusToken{0};

KKRenderGdiPlus::KKRenderGdiPlus()
{
    if (ggdiplusToken == 0)
    {
        Gdiplus::GdiplusStartup(&ggdiplusToken, &gdiplusStartupInput, NULL);
    }
    m_pen = new Gdiplus::Pen(Gdiplus::Color(m_color.a, m_color.r, m_color.g, m_color.b));
    m_font = getFontCache("Microsoft YaHei", 12, Gdiplus::FontStyleRegular);
}

KKRenderGdiPlus::~KKRenderGdiPlus()
{
    delete m_pen;
    // Gdiplus::GdiplusShutdown(m_gdiplusToken);
}

bool KKRenderGdiPlus::Init(void *handle)
{
    if (handle == nullptr)
    {
        return false;
    }
    m_hwnd = (HWND)handle;
    m_hdc = ::GetDC(m_hwnd);
    m_graphics = Gdiplus::Graphics::FromHDC(m_hdc);
    return true;
}

void KKRenderGdiPlus::Release()
{
    if (m_graphics != nullptr)
    {
        m_graphics->ReleaseHDC(m_hdc);
        ::ReleaseDC(m_hwnd, m_hdc);
        m_graphics = nullptr;
    };
}

bool KKRenderGdiPlus::IsReady() { return ggdiplusToken != 0 && m_graphics != nullptr; }

void KKRenderGdiPlus::Clear(KKColor color) { m_graphics->Clear(Gdiplus::Color(color.a, color.r, color.g, color.b)); }

void KKRenderGdiPlus::SetColor(KKColor color)
{
    m_color = color;
    m_pen->SetColor(Gdiplus::Color(color.a, color.r, color.g, color.b));
}

void KKRenderGdiPlus::SetFont(std::string font_name, int font_size, int font_style) { m_font = getFontCache(font_name, font_size, font_style); }

void KKRenderGdiPlus::SetLineCap(int cap)
{
    switch (cap)
    {
    case KK_LINE_CAP_FLAT:
        m_line_cap = Gdiplus::LineCapFlat;
        break;
    case KK_LINE_CAP_ROUND:
        m_line_cap = Gdiplus::LineCapRound;
        break;
    case KK_LINE_CAP_SQUARE:
        m_line_cap = Gdiplus::LineCapSquare;
        break;
    case KK_LINE_CAP_TRIANGLE:
        m_line_cap = Gdiplus::LineCapTriangle;
        break;
    default:
        m_line_cap = Gdiplus::LineCapFlat;
        break;
    }
    m_pen->SetLineCap(m_line_cap, m_line_cap, Gdiplus::DashCapFlat);
}

void KKRenderGdiPlus::SetLineJoin(int join)
{
    switch (join)
    {
    case KK_LINE_JOIN_MITER:
        m_line_join = Gdiplus::LineJoinMiter;
        break;
    case KK_LINE_JOIN_BEVEL:
        m_line_join = Gdiplus::LineJoinBevel;
        break;
    case KK_LINE_JOIN_ROUND:
        m_line_join = Gdiplus::LineJoinRound;
        break;
    case KK_LINE_JOIN_MITER_CLIPPED:
        m_line_join = Gdiplus::LineJoinMiterClipped;
        break;
    default:
        m_line_join = Gdiplus::LineJoinMiter;
        break;
    }
    m_pen->SetLineJoin(m_line_join);
}

void KKRenderGdiPlus::SetLineStyle(int style, int arg1, int arg2)
{
    switch (style)
    {
    case KK_LINE_STYLE_SOLID:
        m_line_style = Gdiplus::DashStyleSolid;
        break;
    case KK_LINE_STYLE_DASHED:
        m_line_style = Gdiplus::DashStyleDash;
        break;
    case KK_LINE_STYLE_DOTTED:
        m_line_style = Gdiplus::DashStyleDot;
        break;
    case KK_LINE_STYLE_DASHDOT:
        m_line_style = Gdiplus::DashStyleDashDot;
        break;
    case KK_LINE_STYLE_DASHDOTDOT:
        m_line_style = Gdiplus::DashStyleDashDotDot;
        break;
    default:
        m_line_style = Gdiplus::DashStyleSolid;
        break;
    }
    m_pen->SetDashStyle(m_line_style);
}

void KKRenderGdiPlus::SetLineWidth(double width)
{
    m_line_width = width;
    m_pen->SetWidth(width);
}

void KKRenderGdiPlus::SetLinearGradientColor(KKPoint start, KKPoint end, KKColor start_color, KKColor end_color) {}

void KKRenderGdiPlus::DrawLine(KKPoint start, KKPoint end) { m_graphics->DrawLine(m_pen, start.x, start.y, end.x, end.y); }
void KKRenderGdiPlus::DrawRect(KKRect rect, bool is_fill)
{
    if (is_fill)
    {
        Gdiplus::SolidBrush brush(Gdiplus::Color(m_color.a, m_color.r, m_color.g, m_color.b));
        m_graphics->FillRectangle(&brush, rect.x1, rect.y1, rect.x2 - rect.x1, rect.y2 - rect.y1);
    }
    else
    {
        m_graphics->DrawRectangle(m_pen, rect.x1, rect.y1, rect.x2 - rect.x1, rect.y2 - rect.y1);
    }
}
void KKRenderGdiPlus::DrawRoundRect(KKRect rect, int radius, bool is_fill)
{
    Gdiplus::GraphicsPath path;
    path.AddLine(rect.x1 + radius, rect.y1, rect.x2 - radius, rect.y1);
    path.AddArc(rect.x2 - radius * 2, rect.y1, radius * 2, radius * 2, 90, -90);
    path.AddLine(rect.x2, rect.y1 + radius, rect.x2, rect.y2 - radius);
    path.AddArc(rect.x2 - radius * 2, rect.y2 - radius * 2, radius * 2, radius * 2, 0, -90);
    path.AddLine(rect.x2 - radius, rect.y2, rect.x1 + radius, rect.y2);
    path.AddArc(rect.x1, rect.y2 - radius * 2, radius * 2, radius * 2, 270, -90);
    path.AddLine(rect.x1, rect.y2 - radius, rect.x1, rect.y1 + radius);
    path.AddArc(rect.x1, rect.y1, radius * 2, radius * 2, 180, -90);
    if (is_fill)
    {
        Gdiplus::SolidBrush brush(Gdiplus::Color(m_color.a, m_color.r, m_color.g, m_color.b));
        m_graphics->FillPath(&brush, &path);
    }
    else
    {
        m_graphics->DrawPath(m_pen, &path);
    }
}
void KKRenderGdiPlus::DrawCircle(KKPoint center, int radius, bool is_fill)
{

    if (is_fill)
    {
        Gdiplus::SolidBrush brush(Gdiplus::Color(m_color.a, m_color.r, m_color.g, m_color.b));
        m_graphics->FillEllipse(&brush, center.x - radius, center.y - radius, radius * 2, radius * 2);
    }
    else
    {
        m_graphics->DrawEllipse(m_pen, center.x - radius, center.y - radius, radius * 2, radius * 2);
    }
}
void KKRenderGdiPlus::DrawArc(KKRect rect, double start_angle, double sweep_angle)
{
    m_graphics->DrawArc(m_pen, rect.x1, rect.y1, rect.x2, rect.y2, start_angle, sweep_angle);
}
void KKRenderGdiPlus::DrawPolygon(std::vector<KKPoint> points, bool is_fill)
{
    Gdiplus::GraphicsPath path;
    path.StartFigure();
    for (int i = 1; i < points.size(); i++)
    {
        path.AddLine(points[i - 1].x, points[i - 1].y, points[i].x, points[i].y);
    }
    path.CloseFigure();
    if (is_fill)
    {
        Gdiplus::SolidBrush brush(Gdiplus::Color(m_color.a, m_color.r, m_color.g, m_color.b));
        m_graphics->FillPath(&brush, &path);
    }
    else
    {
        m_graphics->DrawPath(m_pen, &path);
    }
}

void KKRenderGdiPlus::DrawText(std::string text, KKRect rect, KK_TEXT_ALIGN alignX, KK_TEXT_ALIGN alignY)
{ // 绘制utf8文本
    auto uni_str = text_utf8_to_utf16(text);
    auto brush = Gdiplus::SolidBrush(Gdiplus::Color(m_color.a, m_color.r, m_color.g, m_color.b));
    auto fmt = Gdiplus::StringFormat();
    fmt.SetAlignment((Gdiplus::StringAlignment)alignX);
    fmt.SetLineAlignment((Gdiplus::StringAlignment)alignY);
    m_graphics->DrawString(uni_str.c_str(), uni_str.length(), m_font,
                           Gdiplus::RectF((float)rect.x1, (float)rect.y1, (float)(rect.x2 - rect.x1), (float)(rect.y2 - rect.y1)), &fmt, &brush);
}
void KKRenderGdiPlus::DrawText(std::string text, KKPoint pos)
{ // 绘制utf8文本
    auto uni_str = text_utf8_to_utf16(text);
    auto brush = Gdiplus::SolidBrush(Gdiplus::Color(m_color.a, m_color.r, m_color.g, m_color.b));
    m_graphics->DrawString(uni_str.c_str(), uni_str.length(), m_font, Gdiplus::PointF((float)pos.x, (float)pos.y), &brush);
}

void KKRenderGdiPlus::DrawImage(std::string path, KKRect rect, KK_IMAGE_FIT fit)
{
    auto img = getImageCache(path);
    // draw
    switch (fit)
    {
    case KK_IMAGE_FIT_FILL:
        m_graphics->DrawImage(img, rect.x1, rect.y1, rect.x2 - rect.x1, rect.y2 - rect.y1);
        break;
    case KK_IMAGE_FIT_CONTAIN:
    {
        auto img_rect = KKRect(0, 0, img->GetWidth(), img->GetHeight());
        if (img_rect.x2 > img_rect.x1 && img_rect.y2 > img_rect.y1)
        {
            auto scale_x = (rect.x2 - rect.x1) * 1.0 / (img_rect.x2 - img_rect.x1);
            auto scale_y = (rect.y2 - rect.y1) * 1.0 / (img_rect.y2 - img_rect.y1);
            if (scale_x < scale_y)
            {
                m_graphics->DrawImage(img, rect.x1, rect.y1, rect.x2 - rect.x1, (int)std::round((rect.y2 - rect.y1) * scale_x));
            }
            else
            {
                m_graphics->DrawImage(img, rect.x1, rect.y1, (int)std::round((rect.x2 - rect.x1) * scale_y), rect.y2 - rect.y1);
            }
        }
    }
    break;
    case KK_IMAGE_FIT_COVER:
        break;
    case KK_IMAGE_FIT_REPEAT:
        break;
    default:
        break;
    }
}
void KKRenderGdiPlus::DrawImageBorder(std::string path, KKRect dest, KKSpace space)
{
    auto image = getImageCache(path);
    // 获取原始图片的宽度和高度
    float srcWidth = static_cast<float>(image->GetWidth());
    float srcHeight = static_cast<float>(image->GetHeight());
    Gdiplus::RectF destRect((float)dest.x1, (float)dest.y1, (float)(dest.x2 - dest.x1), (float)(dest.y2 - dest.y1));

    // 定义源图片的九宫格区域
    Gdiplus::RectF srcCorners[4] = {
        Gdiplus::RectF(0, 0, (float)space.x1, (float)space.y1),                             // 左上
        Gdiplus::RectF((float)(srcWidth - space.x2), 0, (float)space.x2, (float)space.y1),  // 右上
        Gdiplus::RectF(0, (float)(srcHeight - space.y2), (float)space.x1, (float)space.y2), // 左下
        Gdiplus::RectF((float)(srcWidth - space.x2), (float)(srcHeight - space.y2), (float)space.x2,
                       (float)space.y2) // 右下
    };

    Gdiplus::RectF srcEdges[4] = {
        Gdiplus::RectF((float)space.x1, 0, (float)(srcWidth - space.x1 - space.x2),
                       (float)space.y1), // 上边
        Gdiplus::RectF(0, (float)space.y1, (float)space.x1,
                       (float)(srcHeight - space.y1 - space.y2)),                                                                 // 左边
        Gdiplus::RectF((float)space.x1, (float)(srcHeight - space.y2), (float)(srcWidth - space.x1 - space.x2), (float)space.y2), // 下边
        Gdiplus::RectF((float)(srcWidth - space.x2), (float)space.y1, (float)space.x2,
                       (float)(srcHeight - space.y1 - space.y2)) // 右边
    };

    Gdiplus::RectF srcCenter((float)space.x1, (float)space.y1, (float)(srcWidth - space.x1 - space.x2),
                             (float)(srcHeight - space.y1 - space.y2)); // 中心

    // 定义目标矩形的九宫格区域
    Gdiplus::RectF destCorners[4] = {
        Gdiplus::RectF((float)destRect.X, (float)destRect.Y, (float)space.x1, (float)space.y1), // 左上
        Gdiplus::RectF(destRect.GetRight() - (float)space.x2, (float)destRect.Y, (float)space.x2,
                       (float)space.y1), // 右上
        Gdiplus::RectF((float)destRect.X, destRect.GetBottom() - (float)space.y2, (float)space.x1,
                       (float)space.y2), // 左下
        Gdiplus::RectF(destRect.GetRight() - (float)space.x2, destRect.GetBottom() - (float)space.y2, (float)space.x2,
                       (float)space.y2) // 右下
    };

    Gdiplus::RectF destEdges[4] = {
        Gdiplus::RectF((float)(destRect.X + space.x1), (float)destRect.Y, (float)(destRect.Width - space.x1 - space.x2), (float)space.y1), // 上边
        Gdiplus::RectF((float)destRect.X, (float)(destRect.Y + space.y1), (float)space.x1,
                       (float)(destRect.Height - space.y1 - space.y2)), // 左边
        Gdiplus::RectF((float)(destRect.X + space.x1), destRect.GetBottom() - (float)space.y2, (float)(destRect.Width - space.x1 - space.x2),
                       (float)space.y2), // 下边
        Gdiplus::RectF(destRect.GetRight() - (float)space.x2, destRect.Y + (float)space.y1, (float)space.x2,
                       destRect.Height - space.y1 - space.y2) // 右边
    };

    Gdiplus::RectF destCenter(destRect.X + space.x1, destRect.Y + space.y1, destRect.Width - space.x1 - space.x2,
                              destRect.Height - space.y1 - space.y2); // 中心

    // 绘制 4 个角
    for (int i = 0; i < 4; i++)
    {
        m_graphics->DrawImage(image, destCorners[i], srcCorners[i].X, srcCorners[i].Y, srcCorners[i].Width, srcCorners[i].Height, Gdiplus::UnitPixel);
    }

    // 绘制 4 条边
    for (int i = 0; i < 4; i++)
    {
        m_graphics->DrawImage(image, destEdges[i], srcEdges[i].X, srcEdges[i].Y, srcEdges[i].Width, srcEdges[i].Height, Gdiplus::UnitPixel);
    }

    // 绘制中心区域
    m_graphics->DrawImage(image, destCenter, srcCenter.X, srcCenter.Y, srcCenter.Width, srcCenter.Height, Gdiplus::UnitPixel);
}

Gdiplus::Image *KKRenderGdiPlus::getImageCache(std::string path)
{
    auto uni_str = text_utf8_to_utf16(path);
    if (m_image_cache.count(path))
    {
        return m_image_cache[path];
    }
    else
    {
        return Gdiplus::Image::FromFile(uni_str.c_str());
    }
}

Gdiplus::Font *KKRenderGdiPlus::getFontCache(std::string font_name, int font_size, int font_style)
{
    // cur
    _font_family_cur = font_name;
    _font_size_cur = font_size;
    _font_style_cur = font_style;
    // find or create
    if (m_font_cache.count(font_name + std::to_string(font_size) + std::to_string(font_style)))
    {
        return m_font_cache[font_name + std::to_string(font_size) + std::to_string(font_style)];
    }
    else
    {
        Gdiplus::FontFamily fontFamily(text_utf8_to_utf16(font_name).c_str());
        return m_font_cache[font_name + std::to_string(font_size) + std::to_string(font_style)] =
                   new Gdiplus::Font(&fontFamily, (Gdiplus::REAL)font_size, (Gdiplus::FontStyle)font_style, Gdiplus::UnitPixel);
    }
}

void KKRenderGdiPlus::DrawRect2(KKRect rect, KKSpace width, KKSpaceColor color, KKSpace line_style)
{
    if (width.x1 == width.y1 && width.x2 == width.y2 && width.x1 == width.x2 && width.y1 == width.y2)
    {
        SetLineWidth(width.x1);
        SetColor(color.x1);
        SetLineStyle(line_style.x1);
        DrawRect(rect, false);
    }
    // 四条边属性不一样，单独绘制
    if (width.x1 > 0)
    {
        SetLineWidth(width.x1);
        SetColor(color.x1);
        SetLineStyle(line_style.x1);
        DrawLine(KKPoint(rect.x1, rect.y1), KKPoint(rect.x1, rect.y2));
    }
    if (width.x2 > 0)
    {
        SetLineWidth(width.x2);
        SetColor(color.x2);
        SetLineStyle(line_style.x2);
        DrawLine(KKPoint(rect.x2, rect.y1), KKPoint(rect.x2, rect.y2));
    }
    if (width.y1 > 0)
    {
        SetLineWidth(width.y1);
        SetColor(color.y1);
        SetLineStyle(line_style.y1);
        DrawLine(KKPoint(rect.x1, rect.y1), KKPoint(rect.x2, rect.y1));
    }
    if (width.y2 > 0)
    {
        SetLineWidth(width.y2);
        SetColor(color.y2);
        SetLineStyle(line_style.y2);
        DrawLine(KKPoint(rect.x1, rect.y2), KKPoint(rect.x2, rect.y2));
    }
}

std::string KKRenderGdiPlus::GetCurFontFamily() { return _font_family_cur; }
int KKRenderGdiPlus::GetCurFontSize() { return _font_size_cur; }
int KKRenderGdiPlus::GetCurFontStyle() { return _font_style_cur; }
